using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Drawing;

namespace WbsEditor
{
    public class WorkBreakdownStructure
    {
        public enum TimeUnit
        {
            Hours,
            Days
        }

        public WorkBreakdownStructure()
        {
            Root = new WbsNode("Task");
            Root.IsRoot = true;
            Root.Title = "(untitled)";
            Root.Wbs = this;

            Title = "(untitled)";
            TID = "";
            Owner = "";
            ConceptLink = "";
            FileName = "";
            if (WbsEditor.Properties.Settings.Default.DefaultDisplayUnit.Equals("Hours"))
                DisplayUnit = TimeUnit.Hours;
            else
                DisplayUnit = TimeUnit.Days;
            if (WbsEditor.Properties.Settings.Default.DefaultPersistUnit.Equals("Hours"))
                PersistUnit = TimeUnit.Hours;
            else
                PersistUnit = TimeUnit.Days;
            float daysToHoursFactor = 8f;
            if (float.TryParse(WbsEditor.Properties.Settings.Default.DefaultDaysToHoursFactor, out daysToHoursFactor))
                DaysToHoursFactor = daysToHoursFactor > 0.1f ? daysToHoursFactor : 8f;
            else
                DaysToHoursFactor = 8f;
        }
        private WbsNode _root;
        public WbsNode Root
        {
            get { return _root; }
            set { _root = value; }
        }

        private string _title;
        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }

        private string _tid;
        public string TID
        {
            get { return _tid; }
            set { _tid = value; }
        }

        private string _owner;
        public string Owner
        {
            get { return _owner; }
            set { _owner = value; }
        }

        private string _conceptLink;
        public string ConceptLink
        {
            get { return _conceptLink; }
            set { _conceptLink = value; }
        }

        private string _fileName;
        public string FileName
        {
            get { return _fileName; }
            set { _fileName = value; }
        }

        private DateTime _lastSaveDate;
        public DateTime LastSaveDate
        {
            get { return _lastSaveDate; }
            set { _lastSaveDate = value; }
        }

        private bool _dirty;
        public bool IsDirty
        {
            get { return _dirty; }
            set { _dirty = value; }
        }

        private string _xmlOriginal;
        public string XmlOriginal
        {
            get { return _xmlOriginal; }
            set { _xmlOriginal = value; }
        }

        public string WbsEditorVersion { get; set; }

        public TimeUnit DisplayUnit { get; set; }
        public TimeUnit PersistUnit { get; set; }

        public static TimeUnit StringToTimeUnit(string unit)
        {
            if (!string.IsNullOrEmpty(unit))
            {
                if (unit.ToLower().Equals("hours"))
                    return TimeUnit.Hours;
            }
            return TimeUnit.Days;
        }

        public static string TimeUnitToString(TimeUnit unit)
        {
            if (TimeUnit.Hours == unit)
                return "Hours";
            return "Days";
        }

        public float DaysToHoursFactor { get; set; }

        public event EventHandler TotalWorkChanged;

        public void RecalculateTotalWork()
        {
            if (null != TotalWorkChanged)
                TotalWorkChanged(this, new EventArgs());
        }

        public WorkBreakdownStructure Clone()
        {
            WorkBreakdownStructure wbs = new WorkBreakdownStructure();
            wbs.ConceptLink = ConceptLink;
            wbs.Title = Title;
            wbs.Owner = Owner;
            wbs.LastSaveDate = LastSaveDate;
            wbs.FileName = FileName;
            wbs.IsDirty = IsDirty;
            wbs.TID = TID;
            wbs.DisplayUnit = DisplayUnit;
            wbs.PersistUnit = PersistUnit;
            wbs.DaysToHoursFactor = DaysToHoursFactor;
            return wbs;
        }

        public WorkBreakdownStructure DeepClone(bool dontKeepOriginalLink)
        {
            WorkBreakdownStructure clone = Clone();
            clone.Root = Root.DeepClone(dontKeepOriginalLink);
            return clone;
        }

        public float ToDisplayTimeUnits(float workInDays)
        {
            if (TimeUnit.Days == DisplayUnit)
                return workInDays;
            return workInDays * DaysToHoursFactor;
        }

        public float FromDisplayTimeUnits(float displayWork)
        {
            float daysToHoursFactor = DaysToHoursFactor;
            if (daysToHoursFactor < 0.1f)
                daysToHoursFactor = 8f;
            if (TimeUnit.Hours == DisplayUnit)
            {
                return displayWork / daysToHoursFactor;
            }
            return displayWork;
        }

        public float ToPersistTimeUnits(float workInDays)
        {
            if (TimeUnit.Days == PersistUnit)
                return workInDays;
            return workInDays * DaysToHoursFactor;
        }

        public float FromPersistTimeUnits(float persistWork)
        {
            float daysToHoursFactor = DaysToHoursFactor;
            if (daysToHoursFactor < 0.1f)
                daysToHoursFactor = 8f;
            if (TimeUnit.Hours == PersistUnit)
            {
                return persistWork / daysToHoursFactor;
            }
            return persistWork;
        }
    }

    public class WbsNode
    {
        public enum NodeLayout
        {
            Vertical,
            Horizontal,
            Paired
        }

        public WbsNode(string workItemType)
        {
            _workItemType = workItemType;
        }

        private string _title = "";
        private int _workItemId = -1;
        private string _workItemType = "Task";
        private bool _isRoot;
        private string _iteration = "/";
        private string _assignedTo = "";
        private int _priority = 1;
        private string _state = "";
        private string[] _possibleNextStates;
        private float _remainingWorkInDays;
        private float _completedWorkInDays;
        private string _description = "";
        private string _pspCode = "";
        private WbsNode _parentNode;
        private bool _isSelected;
        private bool _isCollapsed;
        private string _plausiText = "";
        private List<WbsNode> _childNodes = new List<WbsNode>();
        private WbsNodeLayoutInformation _layoutInfo = new WbsNodeLayoutInformation();
        private bool _isLinkToWbs = false;
        protected WbsNode _original;
        private WorkBreakdownStructure _wbs;

        public bool IsRoot { get { return _isRoot; } set { _isRoot = value; } }
        public string Title { get { return _title; } set { _title = value; if (IsClone) OriginalNode.Title = value; } }
        public bool IsPublished { get { return (WorkItemId > 0); } }
        public int WorkItemId { get { return _workItemId; } set { _workItemId = value; } }
        public string WorkItemType { get { return _workItemType; } set { _workItemType = value; } }
        public string Iteration { get { return _iteration; } set { _iteration = value; if (IsClone) OriginalNode.Iteration = value; } }
        public string AssignedTo { get { return _assignedTo; } set { _assignedTo = value; if (IsClone) OriginalNode.AssignedTo = value; } }
        public int Priority { get { return _priority; } set { _priority = value; if (IsClone) OriginalNode.Priority = value; } }
        public string State { get { return _state; } set { _state = value; if (IsClone) OriginalNode.State = value; } }
        public string[] PossibleNextStates { get { return _possibleNextStates; } set { _possibleNextStates = value; } }
        public float RemainingWorkInDays
        {
            get
            {
                if (this.IsRoot)
                    return this.InnerNodeRemainingWorkInDays;

                return _remainingWorkInDays;
            }
            set
            {
                if (this.IsRoot)
                    return;

                _remainingWorkInDays = value;

                if (IsClone)
                    OriginalNode.RemainingWorkInDays = value;
            }
        }
        public float CompletedWorkInDays
        {
            get
            {
                if (this.IsRoot)
                    return this.InnerNodeCompletedWorkInDays;

                return _completedWorkInDays;
            }
            set
            {
                if (this.IsRoot)
                    return;

                _completedWorkInDays = value; 
                
                if (IsClone) 
                    OriginalNode.CompletedWorkInDays = value;
            }
        }
        public float InnerNodeRemainingWorkInDays { get { return this._childNodes.Sum(c => c.InnerNodeRemainingWorkInDays + c.RemainingWorkInDays); } }
        public float InnerNodeCompletedWorkInDays { get { return this._childNodes.Sum(c => c.InnerNodeCompletedWorkInDays + c.CompletedWorkInDays); } }
        public float DisplayRemainingWorkInDays
        {
            get
            {
                if (this.IsRoot)
                    return this.InnerNodeRemainingWorkInDays;

                return this.RemainingWorkInDays + this.InnerNodeRemainingWorkInDays;
            }
        }
        public float DisplayCompletedWorkInDays
        {
            get
            {
                if (this.IsRoot)
                    return this.InnerNodeCompletedWorkInDays;

                return this.CompletedWorkInDays + this.InnerNodeCompletedWorkInDays;
            }
        }
        public string Description { get { return _description; } set { _description = value; if (IsClone) OriginalNode.Description = value; } }
        public string PspCode { get { return _pspCode; } set { _pspCode = value; } }
        public bool IsSelected { get { return _isSelected; } set { _isSelected = value; } }
        public bool IsCollapsible { get { return (ChildNodes.Count > 0); } }
        public bool IsCollapsed { get { return _isCollapsed; } set { _isCollapsed = value; } }
        public string PlausiText { get { return _plausiText; } set { _plausiText = value; } }
        public List<WbsNode> ChildNodes { get { return _childNodes; } }
        public bool HasChildNodes { get { return _childNodes.Count > 0; } }
        public bool IsLeaf { get { return !HasChildNodes; } }
        public WbsNode Parent { get { return _parentNode; } set { _parentNode = value; } }
        public bool IsLinkToWbs { get { return _isLinkToWbs; } set { _isLinkToWbs = value; } }
        protected WbsNode OriginalNode { get { return _original; } }
        public bool IsClone { get { return (_original != null); } }
        public WorkBreakdownStructure Wbs { get { return _wbs; } set { _wbs = value; } }
        public float RemainingWorkInDisplayUnits
        {
            get { return Wbs.ToDisplayTimeUnits(RemainingWorkInDays); }
            set { RemainingWorkInDays = Wbs.FromDisplayTimeUnits(value); }
        }
        public float CompletedWorkInDisplayUnits
        {
            get { return Wbs.ToDisplayTimeUnits(CompletedWorkInDays); }
            set { CompletedWorkInDays = Wbs.FromDisplayTimeUnits(value); }
        }
        public float RemainingWorkInPersistUnits
        {
            get { return Wbs.ToPersistTimeUnits(DisplayRemainingWorkInDays); }
            set { RemainingWorkInDays = Wbs.FromPersistTimeUnits(value) - this.InnerNodeRemainingWorkInDays; }
        }
        public float CompletedWorkInPersistUnits
        {
            get { return Wbs.ToPersistTimeUnits(DisplayCompletedWorkInDays); }
            set { CompletedWorkInDays = Wbs.FromPersistTimeUnits(value) - this.InnerNodeCompletedWorkInDays; }
        }

        private NodeLayout _nodeLayout;
        public NodeLayout LayoutMode
        {
            get
            {
                if (IsRoot)
                {
                    if (_nodeLayout != NodeLayout.Horizontal)
                    {
                        _nodeLayout = NodeLayout.Horizontal;
                        LayoutInfo.LayoutMode = _nodeLayout;
                    }
                    return NodeLayout.Horizontal;
                }
                return _nodeLayout;
            }
            set
            {
                if (IsRoot)
                {
                    _nodeLayout = NodeLayout.Horizontal;
                    LayoutInfo.LayoutMode = _nodeLayout;
                }
                else
                {
                    _nodeLayout = value;
                    LayoutInfo.LayoutMode = _nodeLayout;
                }
            }
        }

        public WbsNodeLayoutInformation LayoutInfo { get { return _layoutInfo; } }

        public void AddChild(WbsNode node)
        {
            node.Parent = this;
            ChildNodes.Add(node);
        }

        public void AddChild(WbsNode node, int index)
        {
            if (index >= ChildNodes.Count)
            {
                AddChild(node);
            }
            else
            {
                node.Parent = this;
                ChildNodes.Insert(index, node);
            }
        }

        public void AddChildRange(IEnumerable<WbsNode> nodeRange)
        {
            foreach (WbsNode node in nodeRange)
            {
                AddChild(node);
            }
        }

        public void RemoveChild(WbsNode node)
        {
            ChildNodes.Remove(node);
        }

        public bool IsAncestorOf(WbsNode node)
        {
            if (node == null)
                return false;
            WbsNode tmp = node;
            while (tmp != null)
            {
                if (tmp == this)
                    return true;
                tmp = tmp.Parent;
            }
            return false;
        }

        public WbsNode Clone()
        {
            WbsNode clone = new WbsNode(WorkItemType);
            clone.IsRoot = IsRoot;
            clone.Title = Title;
            clone.WorkItemId = WorkItemId;
            clone.Iteration = Iteration;
            clone.AssignedTo = AssignedTo;
            clone.Priority = Priority;
            clone.State = State;
            clone.PossibleNextStates = PossibleNextStates;
            clone.RemainingWorkInDays = RemainingWorkInDays;
            clone.CompletedWorkInDays = CompletedWorkInDays;
            clone.Description = Description;
            clone.PspCode = PspCode;
            clone.IsSelected = false;
            clone.IsCollapsed = false;
            clone.PlausiText = "";
            clone.Parent = null;
            clone.IsLinkToWbs = false;
            clone._original = this;

            return clone;
        }

        public WbsNode Clone(bool dontKeepOriginalLink)
        {
            WbsNode clone = Clone();
            if (dontKeepOriginalLink)
                clone._original = null;
            return clone;
        }

        public WbsNode DeepClone(bool dontKeepOriginalLink)
        {
            WbsNode clone = Clone(dontKeepOriginalLink);

            foreach (WbsNode node in ChildNodes)
            {
                clone.AddChild(node.DeepClone(dontKeepOriginalLink));
            }
            return clone;
        }
    }

    public class WbsNodeLayoutInformation
    {
        public WbsNodeLayoutInformation()
        {
        }

        private Point _position = new Point(0, 0);
        private Size _size = new Size(150, 70);
        private Size _subTreeSize = new Size(100, 50);
        private Rectangle _boundingBox = new Rectangle();
        private Rectangle _collapseBox = new Rectangle();

        public Point Position { get { return _position; } set { _position = value; } }
        public Size Size { get { return _size; } set { _size = value; } }
        public Size SubTreeSize { get { return _subTreeSize; } set { _subTreeSize = value; } }
        public WbsNode.NodeLayout LayoutMode { get; set; }
        public Rectangle BoundingBox
        {
            get
            {
                _boundingBox.X = Position.X;
                _boundingBox.Y = Position.Y;
                _boundingBox.Width = Size.Width;
                _boundingBox.Height = Size.Height;
                return _boundingBox;
            }
        }
        public Rectangle CollapseBox
        {
            get
            {
                if (LayoutMode == WbsNode.NodeLayout.Horizontal
                    || LayoutMode == WbsNode.NodeLayout.Paired)
                {
                    _collapseBox.X = Position.X + Size.Width/2 - 4;
                    _collapseBox.Y = Position.Y + Size.Height - 2;
                    _collapseBox.Width = 8;
                    _collapseBox.Height = 8;
                }
                else
                {
                    _collapseBox.X = Position.X + 6;
                    _collapseBox.Y = Position.Y + Size.Height - 2;
                    _collapseBox.Width = 8;
                    _collapseBox.Height = 8;
                }
                return _collapseBox;
            }
        }
    }
}
